package models

import (
	"strings"

	"github.com/astaxie/beego/logs"
	"github.com/astaxie/beego/orm"

	"cvevulner/common"
)

func GetOriginUpstream(cveNum string) (v OriginUpstreamJoinData, err error) {
	o := orm.NewOrm()
	var vs []OriginUpstreamJoinData
	_, err = o.Raw(
		`SELECT a.*,b.en_desc,b.zh_desc,c.conf_id,d.impact_id,e.source spoc,e.date,e.path,
e.dbindex,e.url,e.desc,f.fix_id,f.detail FROM cve_origin_upstream a 
LEFT JOIN cve_origin_upstream_desc b ON a.cve_id = b.cve_id 
LEFT JOIN cve_origin_upstream_config c ON a.cve_id = c.cve_id 
LEFT JOIN cve_origin_upstream_impact d ON a.cve_id = d.cve_id 
LEFT JOIN cve_origin_upstream_poc e ON a.cve_id = e.cve_id 
LEFT JOIN cve_origin_upstream_fix_suggest f ON a.cve_id = f.cve_id 
WHERE a.cve_num = ? and d.source = "nvd" ORDER BY a.cve_id DESC`, cveNum,
	).QueryRows(&vs)
	if err != nil {
		logs.Error("GetOriginUpstream, cveNum: ", cveNum, ", Query failed")
	}
	if len(vs) > 0 {
		v = vs[0]
	}

	return
}

func QueryOriginUpstreamCount() int64 {
	sql := "SELECT count(*) total FROM cve_origin_upstream where source = 0"

	res := struct {
		Total int64
	}{}
	o := orm.NewOrm()
	err := o.Raw(sql).QueryRow(&res)
	if err != nil {
		return 0
	}
	return res.Total
}

func QueryOriginUpstream(currentPage, pageSize int) (v []OriginUpstreamJoinData) {
	startSize := (currentPage - 1) * pageSize
	o := orm.NewOrm()

	_, err := o.Raw(
		`SELECT a.*,b.en_desc,b.zh_desc,c.conf_id,d.impact_id,e.source spoc,e.date,e.path,
e.dbindex,e.url,e.desc,f.fix_id,f.detail FROM cve_origin_upstream a 
LEFT JOIN cve_origin_upstream_desc b ON a.cve_id = b.cve_id 
LEFT JOIN cve_origin_upstream_config c ON a.cve_id = c.cve_id 
LEFT JOIN cve_origin_upstream_impact d ON a.cve_id = d.cve_id 
LEFT JOIN cve_origin_upstream_poc e ON a.cve_id = e.cve_id 
LEFT JOIN cve_origin_upstream_fix_suggest f ON a.cve_id = f.cve_id 
WHERE a.source = 0 and d.source = "nvd" ORDER BY a.cve_id DESC LIMIT ? OFFSET ?`,
		pageSize, startSize,
	).QueryRows(&v)
	if err != nil {
		logs.Error("GetOriginUpstream, cve_origin_upstream,failed: ", err)
	}

	return
}

func CveOriginDetail(v []OriginUpstreamJoinData, c chan<- []common.CveOriginData) {
	var res = make([]common.CveOriginData, len(v))
	for i := range v {
		res[i] = v[i].ToDetailSummary()
	}

	c <- res

	return
}

func QueryOriginPackageUrl(cveid int64) (err error, list []OriginUpstreamPackageUrl) {
	o := orm.NewOrm()
	_, err = o.Raw(
		"select * from cve_origin_upstream_package_url where cve_id = ?", cveid,
	).QueryRows(&list)

	return
}

func GetOriginDesc(cveId int64, oud *OriginUpstreamDesc) (err error) {
	o := orm.NewOrm()
	err = o.Raw(
		"select * FROM cve_origin_upstream_desc WHERE cve_id=?", cveId,
	).QueryRow(oud)

	return
}

func GetOriginConfig(cveId int64, ouc *OriginUpstreamConfig) (err error) {
	o := orm.NewOrm()
	err = o.Raw(
		"select * FROM cve_origin_upstream_config WHERE cve_id=?", cveId,
	).QueryRow(ouc)

	return
}

func GetOriginConfigNode(configId int64, oucn *[]OriginUpstreamConfigNode) (num int64, err error) {
	o := orm.NewOrm()
	num, err = o.Raw(
		"select * FROM cve_origin_upstream_config_node WHERE conf_id=?", configId,
	).QueryRows(oucn)

	return
}

func GetOriginConfigNodeCpe(nodeId int64, oucnc *[]OriginUpstreamConfigNodeCpe) (num int64, err error) {
	o := orm.NewOrm()
	num, err = o.Raw(
		"select * FROM cve_origin_upstream_config_node_cpe WHERE node_id=?", nodeId,
	).QueryRows(oucnc)

	return
}

func GetOriginPoc(cveId int64, oup *OriginUpstreamPoc) (err error) {
	o := orm.NewOrm()
	err = o.Raw(
		"select * FROM cve_origin_upstream_poc WHERE cve_id=?", cveId,
	).QueryRow(oup)

	return
}

func GetOriginEvent(cveId int64, oue *[]OriginUpstreamEvent) (err error) {
	o := orm.NewOrm()
	_, err = o.Raw(
		"select * FROM cve_origin_upstream_event WHERE cve_id=?", cveId,
	).QueryRows(oue)

	return
}

func GetOriginReference(cveId int64, our *[]OriginUpstreamReference) (num int64, err error) {
	o := orm.NewOrm()
	num, err = o.Raw(
		"select * FROM cve_origin_upstream_reference WHERE cve_id=?", cveId,
	).QueryRows(our)

	return
}

func GetOriginVulType(cveId int64, ouv *[]OriginUpstreamVulType) (err error) {
	o := orm.NewOrm()
	_, err = o.Raw(
		"select * FROM cve_origin_upstream_vul_type WHERE cve_id=?", cveId,
	).QueryRows(ouv)

	return
}

func GetOriginFixSuggest(cveId int64, ouf *OriginUpstreamFixSuggest) (err error) {
	o := orm.NewOrm()
	err = o.Raw(
		"select * FROM cve_origin_upstream_fix_suggest WHERE cve_id=?", cveId,
	).QueryRow(ouf)

	return
}

func GetOriginFixSuggestRef(fixId int64, oufr *[]OriginUpstreamFixSuggestRef) (num int64, err error) {
	o := orm.NewOrm()
	num, err = o.Raw(
		"select * FROM cve_origin_upstream_fix_suggest_ref WHERE fix_id=?", fixId,
	).QueryRows(oufr)

	return
}

func GetOriginFixSuggestRefTag(fixRefId int64, oufrt *[]OriginUpstreamFixSuggestRefTag) (num int64, err error) {
	o := orm.NewOrm()
	num, err = o.Raw(
		"select * FROM cve_origin_upstream_fix_suggest_ref_tag WHERE fix_ref_id=?", fixRefId,
	).QueryRows(oufrt)

	return
}

type OriginUpstreamJoinData struct {
	OriginUpstream
	FixId         int64  `orm:"column(fix_id)"`
	ConfId        int64  `orm:"column(conf_id)"`
	ImpactId      int64  `orm:"column(impact_id)"`
	Url           string `orm:"column(url)"`
	Spoc          string `orm:"column(spoc)"`
	Date          string `orm:"column(date)"`
	Path          string `orm:"column(path)"`
	Desc          string `orm:"column(desc)"`
	Detail        string `orm:"column(detail)"`
	Dbindex       string `orm:"column(dbindex)"`
	EnDescription string `orm:"column(en_desc)"`
	ZhDescription string `orm:"column(zh_desc)"`
}

func (ou OriginUpstreamJoinData) ToDetailSummary() (cod common.CveOriginData) {
	cod.Ids = ou.CveNum
	cod.CveNum = ou.CveNum
	cod.UpdateType = ou.UpdateType
	cod.PackName = strings.Split(ou.PackName, ",")
	cod.CvePackName = strings.Split(ou.CvePackName, ",")
	cod.Title = ou.Title
	cod.CnnvdID = ou.CnnvdID
	cod.CnvdID = ou.CnvdID
	cod.PublishedDate = ou.PublishedDate
	cod.VulStatus = ou.VulStatus
	cod.GetTime = ou.FirstPerTime
	cod.EndGetTime = ou.FirstGetTime
	cod.AffectProduct = strings.Split(ou.AffectProduct, ",")
	cod.Credibility = ou.Credibility
	cod.Version = ou.Version

	cod.Description.EnDesc = ou.EnDescription
	cod.Description.ZhDesc = ou.ZhDescription

	var cfgNode []OriginUpstreamConfigNode
	var cf common.CveConfigurations
	_, _ = GetOriginConfigNode(ou.ConfId, &cfgNode)
	for _, nodes := range cfgNode {
		var cn = common.ConfNodes{
			Operator: nodes.Operator,
		}
		var cfgNodeCpe []OriginUpstreamConfigNodeCpe
		num, _ := GetOriginConfigNodeCpe(nodes.NodeId, &cfgNodeCpe)
		cn.Cpe = make([]common.NodeCpe, num)
		for i, cpe := range cfgNodeCpe {
			cn.Cpe[i] = common.NodeCpe{
				Vulnerable:     cpe.Vulnerable,
				CpeMatchString: cpe.CpeMatchString,
				Cpe23Uri:       cpe.Cpe23Uri,
			}
		}
		cf.Nodes = append(cf.Nodes, cn)
	}
	cod.Configurations = cf

	cod.Poc.Url = ou.Url
	cod.Poc.Date = ou.Date
	cod.Poc.Source = ou.Spoc
	cod.Poc.Path = ou.Path
	cod.Poc.Desc = ou.Desc
	cod.Poc.Dbindex = ou.Dbindex

	var oue []OriginUpstreamEvent
	_ = GetOriginEvent(ou.CveId, &oue)
	cod.Event = make([]common.CveEvent, len(oue))
	for i, v := range oue {
		cod.Event[i] = common.CveEvent{
			Title:       v.Title,
			Date:        v.Date,
			Description: v.Description,
			Url:         v.Url,
		}
	}

	var our []OriginUpstreamReference
	_, _ = GetOriginReference(ou.CveId, &our)
	cod.ReferenceData = make([]common.CveReferenceData, len(our))
	for i, v := range our {
		cod.ReferenceData[i] = common.CveReferenceData{
			Url:       v.Url,
			Name:      v.Name,
			Refsource: v.Refsource,
			SourceUrl: v.SourceUrl,
			Tags:      strings.Split(v.Tags, ","),
		}
	}

	var ouv []OriginUpstreamVulType
	_ = GetOriginVulType(ou.CveId, &ouv)
	cod.VulType = make([]common.CveVulType, len(ouv))
	for i, v := range ouv {
		cod.VulType[i] = common.CveVulType{
			Cwe: v.Cwe,
			En:  v.EnDesc,
			Zh:  v.ZhDesc,
		}
	}

	var oufr []OriginUpstreamFixSuggestRef
	_, _ = GetOriginFixSuggestRef(ou.FixId, &oufr)
	for _, ref := range oufr {
		var fr common.FixReferences
		fr.Refsource = ref.Refsource
		fr.Name = ref.Name
		fr.Url = ref.Url
		var oufrt []OriginUpstreamFixSuggestRefTag
		_, _ = GetOriginFixSuggestRefTag(ref.FixRefId, &oufrt)
		for _, tag := range oufrt {
			fr.Tags = append(fr.Tags, tag.Name)
		}
		cod.FixSuggest.References = append(cod.FixSuggest.References, fr)
	}
	cod.FixSuggest.Detail = ou.Detail

	patch, _ := QueryCveOriginPatchInfo(ou.CveNum)
	cod.Patch = make([]common.CveOriginPatch, len(patch))
	for i, v := range patch {
		cod.Patch[i] = common.CveOriginPatch{
			Package: v.Package, FixVersion: v.FixVersion, FixPatch: v.FixPatch, BreakPatch: v.BreakPatch, Source: v.Source, Branch: v.Branch,
		}
	}

	_, list := QueryOriginPackageUrl(ou.CveId)
	p := common.CvePackageUrl{}
	for _, l := range list {
		switch l.Source {
		case common.GO:
			p.GO = append(p.GO, common.PackageUrlField{
				Purl: l.Purl, VersionStartIncluding: l.VersionStartIncluding,
				VersionStartExcluding: l.VersionStartExcluding, VersionEndExcluding: l.VersionEndExcluding,
				VersionEndIncluding: l.VersionEndIncluding,
			})
		case common.Gemnasium:
			p.Gemnasium = append(p.Gemnasium, common.PackageUrlField{
				Purl: l.Purl, VersionStartIncluding: l.VersionStartIncluding,
				VersionStartExcluding: l.VersionStartExcluding, VersionEndExcluding: l.VersionEndExcluding,
				VersionEndIncluding: l.VersionEndIncluding,
			})
		case common.GithubAdvistory:
			p.GithubAdvistory = append(p.GithubAdvistory, common.PackageUrlField{
				Purl: l.Purl, VersionStartIncluding: l.VersionStartIncluding,
				VersionStartExcluding: l.VersionStartExcluding, VersionEndExcluding: l.VersionEndExcluding,
				VersionEndIncluding: l.VersionEndIncluding,
			})
		case common.Huntr:
			p.Huntr = append(p.Huntr, common.PackageUrlField{
				Purl: l.Purl, VersionStartIncluding: l.VersionStartIncluding,
				VersionStartExcluding: l.VersionStartExcluding, VersionEndExcluding: l.VersionEndExcluding,
				VersionEndIncluding: l.VersionEndIncluding,
			})

		}
	}

	cod.PackageUrl = p

	return
}
